package com.antifly.common.utils;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ArrayUtil;
import com.antifly.common.annotation.RelMapper;
import com.antifly.common.constant.CommonConstant;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @author LuoJiaHui
 * @date 2023/4/25 13:58
 * @description
 */
public class RelationMapperUtils {
    /**
     * Entity and Dto Mapper
     * entityToDto ture  : Entity To Dto (defult)
     * false : Dto To Entry
     * Rule:
     * 实现相互转换前提: Dto field name(dto和entry的field name相同并且 类上有@RelMapper) 或 field的@RelMapper
     * (value="Entity field name") 满足其一即可转换
     *
     * @param entity 实体类
     * @param dto    DTO类
     * @return
     * @throws Exception
     */
    public static Object entityToDto(Object entity, Object dto) {
        try {
            return EnAndDtoMapper(entity, dto, true);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return new Object();
    }

    public static Object DtoToEntity(Object dto, Object entity) {
        try {
            return EnAndDtoMapper(entity, dto, false);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return new Object();
    }

    //last version
    public static Object EnAndDtoMapper(Object entry, Object dto, boolean enToDto) throws Exception {
        if (enToDto ? entry == null : dto == null) {
            return null;
        }
        Class<?> entryClazz = entry.getClass();    //获取entity类
        Class<?> dtoClazz = dto.getClass();    //获取dto类
        boolean dtoExistAnnotation = dtoClazz.isAnnotationPresent(RelMapper.class);    //判断类上面是否有自定义注解
        Field[] dtoFields = getAllFields(dtoClazz);   //dto fields
        Field[] entityFields = getAllFields(entryClazz);    //entity fields
        Method[] entityMethods = getAllMethod(entryClazz);    //entity methods
        Method[] dtoMethods = getAllMethod(dtoClazz);    //dto methods
        String mName, fieldName, dtoFieldType = null, entFieldType = null, dtoFieldName = null;
        for (Method m : (enToDto ? dtoMethods : entityMethods)) {    //当 enToDto=true 此时是Entity转为Dto，遍历dto的属性
            if ((mName = m.getName()).startsWith("set")) {    //只进set方法
                fieldName = mName.toLowerCase().charAt(3) + mName.substring(4);  //通过set方法获得dto的属性名
                toHere:
                for (Field fd : dtoFields) {
                    fd.setAccessible(true);    //setAccessible是启用和禁用访问安全检查的开关
                    if (fd.isAnnotationPresent(RelMapper.class) || dtoExistAnnotation) {    //判断field上注解或类上面注解是否存在
                        //获取与Entity属性相匹配的映射值(两种情况：1.该field上注解的value值(Entity的field name 和Dto 的field name 不同)  2
                        // .该field本身(本身则是Entity的field name 和Dto 的field name 相同))
                        String dtoMapName = fd.isAnnotationPresent(RelMapper.class) ?
                                (fd.getAnnotation(RelMapper.class).value().equals("") ? fd.getName() :
                                        fd.getAnnotation(RelMapper.class).value()) : fd.getName();
                        if (((enToDto ? fd.getName() : dtoMapName)).equals(fieldName)) {
                            dtoFieldType =
                                    fd.getGenericType().toString().substring(fd.getGenericType().toString().lastIndexOf(".") + 1); // 获取dto属性的类型(如 private String field 结果 = String)
                            for (Field fe : entityFields) {
                                fe.setAccessible(true);
                                if (fe.getName().equals(enToDto ? dtoMapName : fieldName)) {//遍历Entity
                                    // 类的属性与dto属性注解中的value值匹配
                                    entFieldType =
                                            fe.getGenericType().toString().substring(fe.getGenericType().toString().lastIndexOf(".") + 1); //获取Entity类属性类型
                                    dtoFieldName = enToDto ? dtoMapName : fd.getName();
                                    break toHere;
                                }
                            }
                        }
                    }
                }
                if (dtoFieldName != null && !dtoFieldName.equals("null")) {
                    for (Method md : (enToDto ? entityMethods : dtoMethods)) {
                        if (md.getName().toUpperCase().equals("GET" + dtoFieldName.toUpperCase())) {
                            dtoFieldName = null;
                            if (md.invoke(enToDto ? entry : dto) == null) {
                                break;
                            } //去空操作
                            //Entity类field 与Dto类field类型不一致通过TypeProcessor处理转换
                            Object value = (entFieldType.equals(dtoFieldType)) ? md.invoke(enToDto ? entry : dto) :
                                    TypeProcessor(entFieldType, dtoFieldType, md.invoke(enToDto ? entry : dto), enToDto);
                            m.invoke(enToDto ? dto : entry, value); //得到field的值 通过invoke()赋值给要转换类的对应属性
                            break;
                        }
                    }
                }
            }
        }
        return enToDto ? dto : entry;
    }

    //类型转换处理
    public static Object TypeProcessor(String entFieldType, String dtoFieldType, Object obj, boolean enToDto) {
        if (!entFieldType.equals(dtoFieldType)) {
            switch (entFieldType) {
                case "Date":
                    return (enToDto) ? DateUtil.formatDate((Date) obj) : DateUtil.parseDate(obj.toString());
                case "Timestamp":
                    return new SimpleDateFormat(CommonConstant.DATE_TIME_FORMAT_PATTERN).format((Timestamp) obj);
                case "Integer":
                    return (enToDto) ? obj.toString() : Integer.parseInt((String) obj);
            }
        }
        return obj;
    }

    private static Field[] getAllFields(Class<?> clazz) {
        // 获取对象及其父类所有属性
        Field[] fields = null;
        for (; clazz != Object.class; clazz = clazz.getSuperclass()) {//向上循环 遍历父类
            Field[] field = clazz.getDeclaredFields();
            // huTool工具包，合并两个数组
            fields = ArrayUtil.addAll(fields, field);
        }
        return fields;
    }

    private static Method[] getAllMethod(Class<?> clazz) {
        // 获取对象及其父类所有方法
        Method[] methods = null;
        for (; clazz != Object.class; clazz = clazz.getSuperclass()) {//向上循环 遍历父类
            Method[] method = clazz.getDeclaredMethods();
            // huTool工具包，合并两个数组
            methods = ArrayUtil.addAll(methods, method);
        }
        return methods;
    }
}
